[BE] SISC1-150 [FEAT] 회원정보 관리 및 로그아웃 구현#62
Hidden character warning
Conversation
Walkthrough로그아웃 엔드포인트 및 RefreshToken 엔티티/리포지토리를 추가하고, LoginService.logout 구현과 사용자 정보 수정(PATCH /user/{userId}) 기능 및 관련 DTO·서비스를 도입합니다. 관련 유닛 테스트들이 추가되어 흐름과 엣지 케이스를 검증합니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant OauthLoginController
participant LoginService
participant JwtParser
participant RefreshTokenRepository
Client->>OauthLoginController: POST /auth/logout (Authorization: Bearer {token})
OauthLoginController->>OauthLoginController: Authorization 헤더 검증 및 토큰 추출
OauthLoginController->>LoginService: logout(accessToken)
LoginService->>JwtParser: parse accessToken -> userId
LoginService->>RefreshTokenRepository: deleteByUserId(userId)
RefreshTokenRepository-->>LoginService: 삭제 완료
LoginService-->>OauthLoginController: 완료
OauthLoginController->>OauthLoginController: refresh token 쿠키 삭제(maxAge=0)
OauthLoginController-->>Client: 200 OK + 성공 메시지
sequenceDiagram
participant Client
participant UserController
participant UserService
participant UserRepository
participant PasswordEncoder
Client->>UserController: PATCH /user/{userId} (UserUpdateRequest)
UserController->>UserService: updateUser(userId, request)
UserService->>UserRepository: findById(userId)
alt 사용자 존재
UserService->>UserService: name/phone 업데이트(제공된 필드만)
alt password 제공
UserService->>PasswordEncoder: encode(password)
PasswordEncoder-->>UserService: encodedPassword
UserService->>UserRepository: save(updatedUser)
else
UserService->>UserRepository: save(updatedUser)
end
UserRepository-->>UserService: 저장 완료
UserService-->>UserController: 성공
else 사용자 없음
UserService-->>UserController: 예외(사용자 없음)
end
UserController-->>Client: 200 OK 또는 에러
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (7)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginService.java (1)
3-3: 사용되지 않는 import를 제거하세요.
HttpServletRequest가 import되었지만 인터페이스에서 사용되지 않습니다.-import jakarta.servlet.http.HttpServletRequest;backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (1)
329-360: 모든 필드가 null일 때 save() 호출이 필요한지 검토하세요.Line 358에서 모든 필드가 null이어도
userRepository.save(existingUser)가 호출됩니다. 변경사항이 없을 때는 save를 호출하지 않는 것이 더 효율적일 수 있습니다.서비스 구현부에서 변경사항이 있을 때만 save를 호출하도록 개선을 고려해보세요:
// 서비스 구현 예시 public void updateUser(UUID userId, UserUpdateRequest request) { User user = userRepository.findById(userId) .orElseThrow(() -> new IllegalArgumentException("User not found")); boolean hasChanges = false; if (request.getUsername() != null) { user.setName(request.getUsername()); hasChanges = true; } // ... other fields if (hasChanges) { userRepository.save(user); } }backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (3)
3-3: 사용되지 않는 import를 제거하세요.
HttpServletRequest가 import되었지만 사용되지 않습니다.-import jakarta.servlet.http.HttpServletRequest;
22-22: 사용되지 않는 import를 제거하세요.
java.sql.Ref가 import되었지만 사용되지 않습니다.-import java.sql.Ref;
60-65: 로그아웃 구현에서 예외 처리를 추가하세요.
jwtParser.getUserIdFromToken(accessToken)이 잘못된 토큰으로 예외를 발생시킬 수 있습니다. 이미 로그아웃된 사용자나 만료된 토큰의 경우에도 graceful하게 처리하는 것이 좋습니다.@Override public void logout(String accessToken) { + try { UUID userId = jwtParser.getUserIdFromToken(accessToken); refreshTokenRepository.deleteByUserId(userId); log.info("로그아웃 완료: userId={}", userId); + } catch (Exception e) { + log.warn("로그아웃 처리 중 토큰 파싱 실패: {}", e.getMessage()); + // 이미 만료되거나 유효하지 않은 토큰도 로그아웃 성공으로 간주 + } }backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
109-109: 불필요한 save() 호출 제거 가능메서드에
@Transactional을 추가하면 JPA의 더티 체킹(dirty checking)이 자동으로 변경사항을 감지하여 저장하므로 명시적인save()호출이 불필요합니다.
@Transactional을 추가한 후 다음과 같이 수정할 수 있습니다:- userRepository.save(user); + // @Transactional에 의해 자동으로 변경사항이 저장됨backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (1)
9-11: 입력 검증 어노테이션 추가 권장DTO에 Bean Validation 어노테이션을 추가하여 컨트롤러 레벨에서 입력 값을 검증하는 것이 좋습니다. 특히 비밀번호는 강도 요구사항이 있을 수 있고, 전화번호는 형식 검증이 필요합니다.
다음과 같이 개선할 수 있습니다:
+import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + @Getter @Setter public class UserUpdateRequest { + @Size(min = 2, max = 50, message = "이름은 2자 이상 50자 이하여야 합니다") private String username; + + @Pattern(regexp = "^[0-9]{10,11}$", message = "올바른 전화번호 형식이 아닙니다") private String phoneNumber; + + @Pattern( + regexp = "^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,}$", + message = "비밀번호는 8자 이상, 영문, 숫자, 특수문자를 포함해야 합니다" + ) private String password; // 변경 시에만 받기 }컨트롤러에서
@Valid어노테이션을 추가하는 것도 잊지 마세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/LoginService.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java(3 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/Oauth2Service.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserService.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(3 hunks)backend/src/test/java/org/sejongisc/backend/auth/controller/OauthLoginControllerTest.java(3 hunks)backend/src/test/java/org/sejongisc/backend/auth/service/LoginServiceImplTest.java(4 hunks)backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (2)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (1)
Slf4j(23-199)backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (1)
Slf4j(28-110)
backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java (1)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
RefreshToken(7-15)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Backend Gate
🔇 Additional comments (10)
backend/src/main/java/org/sejongisc/backend/auth/service/Oauth2Service.java (1)
10-11: LGTM!공백 추가만 있고 기능적 변경은 없습니다.
backend/src/main/java/org/sejongisc/backend/auth/service/LoginService.java (1)
10-10: LGTM!로그아웃 메서드 시그니처가 올바르게 선언되었습니다.
backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (2)
273-308: LGTM!회원정보 수정 성공 케이스를 올바르게 테스트하고 있습니다. 이름, 전화번호, 비밀번호 변경이 모두 검증되고 있습니다.
310-327: LGTM!존재하지 않는 사용자에 대한 예외 처리를 올바르게 테스트하고 있습니다.
backend/src/main/java/org/sejongisc/backend/user/service/UserService.java (1)
5-5: LGTM!
updateUser메서드가 인터페이스에 올바르게 선언되었습니다.Also applies to: 9-9, 16-16
backend/src/test/java/org/sejongisc/backend/auth/service/LoginServiceImplTest.java (2)
143-169: LGTM!OAuth 로그인 사용자(passwordHash가 null)가 일반 로그인을 시도할 때 예외가 발생하는 것을 올바르게 테스트하고 있습니다.
172-188: LGTM!로그아웃 플로우가 올바르게 테스트되고 있습니다. JwtParser를 통한 userId 추출과 RefreshTokenRepository를 통한 토큰 삭제가 검증되고 있습니다.
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
14-14: 토큰 길이 제한 검토 필요JWT 토큰의 길이는 payload 크기에 따라 달라질 수 있습니다. 500자가 충분한지 확인이 필요합니다. 일반적으로 JWT 토큰은 200-500자 정도이지만, 클레임이 많아지면 더 길어질 수 있습니다.
실제 생성되는 RefreshToken의 길이를 확인하고, 여유를 두어 1000자 정도로 설정하는 것을 권장합니다:
- @Column(nullable = false, length = 500) + @Column(nullable = false, length = 1000) private String token;backend/src/test/java/org/sejongisc/backend/auth/controller/OauthLoginControllerTest.java (2)
242-260: OAuth 초기화 테스트 구현 우수여러 OAuth 제공자(Google, Kakao, GitHub)에 대한 초기화 엔드포인트를 효율적으로 테스트하고 있습니다.
containsString을 사용하여 각 제공자의 인증 URL이 올바르게 포함되는지 검증하는 것은 좋은 접근 방식입니다.
309-332: 로그아웃 테스트 커버리지 우수로그아웃 기능에 대한 테스트가 잘 작성되었습니다:
- Line 320:
verify를 사용하여loginService.logout()이 정확히 한 번 호출되었는지 확인- Line 331: Authorization 헤더가 없을 때
logout()이 호출되지 않았는지 확인이러한 검증은 보안상 중요한 부분으로, 올바른 테스트 방식입니다.
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Outdated
Show resolved
Hide resolved
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java
Outdated
Show resolved
Hide resolved
discipline24
left a comment
There was a problem hiding this comment.
GPT 냄새가 여기저기서 나는데 조금 신경써주세요~
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
17-17: 필드명을 camelCase로 변경 필요 (재확인)이전 리뷰에서 지적된 필드명
userid가 아직 수정되지 않았습니다. Java 네이밍 컨벤션에 따라userId로 변경해야 합니다.또한
RefreshTokenRepository의 메서드명(findByUserid,deleteByUserId)에 일관성이 없으므로 함께 수정이 필요합니다.다음과 같이 수정하세요:
@Id @Column(name = "user_id", nullable = false, columnDefinition = "uuid") - private UUID userid; + private UUID userId;
🧹 Nitpick comments (1)
backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java (1)
7-21: 토큰 만료 시간 필드 추가 고려RefreshToken에 만료 시간을 저장하는
expiresAt또는createdAt필드를 추가하면 토큰 생명주기 관리가 용이해집니다. 이를 통해 만료된 토큰을 주기적으로 정리하거나 검증 시 활용할 수 있습니다.예시:
@Column(name = "expires_at", nullable = false) private LocalDateTime expiresAt;
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java(3 hunks)backend/src/main/java/org/sejongisc/backend/auth/entity/RefreshToken.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/repository/RefreshTokenRepository.java(1 hunks)backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java(3 hunks)backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java(2 hunks)backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java(1 hunks)backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java(4 hunks)backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (2)
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (1)
Slf4j(24-213)backend/src/main/java/org/sejongisc/backend/common/auth/springsecurity/JwtAuthenticationFilter.java (1)
Slf4j(28-110)
🔇 Additional comments (7)
backend/src/main/java/org/sejongisc/backend/common/exception/ErrorCode.java (1)
53-53: LGTM!입력값 검증을 위한 에러 코드가 적절하게 추가되었습니다. 기존 패턴과 일관성이 있습니다.
backend/src/main/java/org/sejongisc/backend/auth/controller/OauthLoginController.java (1)
177-210: LGTM!로그아웃 엔드포인트가 잘 구현되었습니다:
- 헤더 검증이 적절히 수행됩니다
- 예외 처리를 통해 멱등성이 보장됩니다 (이미 만료된 토큰도 200 OK 반환)
- Refresh Token 쿠키가 항상 삭제됩니다
이전 리뷰의 피드백이 모두 반영되었습니다.
backend/src/main/java/org/sejongisc/backend/user/dto/UserUpdateRequest.java (1)
1-12: LGTM!DTO 필드명이 엔티티와 일치하며 구조가 명확합니다. 이전 리뷰에서 제기된 필드명 불일치 문제가 해결되었습니다.
backend/src/main/java/org/sejongisc/backend/auth/service/LoginServiceImpl.java (1)
60-66: LGTM!로그아웃 메서드가 적절히 구현되었습니다:
@Transactional어노테이션이 적용되어 트랜잭션 안전성이 보장됩니다- JWT에서 userId를 추출하고 refresh token을 삭제하는 로직이 명확합니다
- 로그도 적절히 기록됩니다
backend/src/main/java/org/sejongisc/backend/user/controller/UserController.java (1)
51-69: LGTM!회원정보 수정 엔드포인트가 안전하게 구현되었습니다:
@AuthenticationPrincipal을 통한 인증 확인- 본인 확인 로직으로 권한 검증 (Lines 62-65)
@Valid어노테이션으로 입력값 검증이전 리뷰에서 제기된 인증/인가 보안 취약점이 모두 해결되었습니다.
backend/src/main/java/org/sejongisc/backend/user/service/UserServiceImpl.java (1)
100-126: LGTM!회원정보 수정 메서드가 견고하게 구현되었습니다:
@Override와@Transactional어노테이션이 적절히 적용되었습니다CustomException을 사용하여 예외 처리 일관성을 유지합니다 (Line 104)- 모든 필드에 대해
trim()과isEmpty()검증을 수행합니다 (Lines 107-121)- 비밀번호는 유효한 경우에만 인코딩하여 저장합니다
이전 리뷰에서 제기된 모든 문제(필수 어노테이션 누락, 예외 처리 불일치, 입력 검증 부재)가 해결되었습니다.
backend/src/test/java/org/sejongisc/backend/user/service/UserServiceImplTest.java (1)
273-354: LGTM!회원정보 수정 기능에 대한 테스트가 포괄적으로 작성되었습니다:
- 성공 케이스: 모든 필드 업데이트 검증 (Lines 273-308)
- 실패 케이스: 존재하지 않는 사용자 예외 처리 검증 (Lines 310-321)
- 엣지 케이스: 모든 필드가 null일 때 변경 없음 검증 (Lines 323-354)
Mock 상호작용과 assertion이 적절하며, 기존 테스트 패턴과 일관성이 있습니다.
구현 내용
controller
service
repository (dao)
Summary by CodeRabbit
새로운 기능
에러/검증
테스트